// $Id: DataMapper.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 NourY Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Generic database oriented implementation of Data Mappers.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Design.Serialization;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Reflection;
using Salamanca.Collections;
using Salamanca.DataAccess.Collections;
using Salamanca.DataRules;

namespace Salamanca.DataAccess.Data
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Provides data for database oriented <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" />s events.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public sealed class DataMapperEventArgs<TDomainEntity>:
        EventArgs
        where TDomainEntity:
            IDomainEntity
    {

        /// <summary>Constructor.</summary>
        public DataMapperEventArgs(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            _DomainEntity=domainModel;
            _Commands=new ReadOnlyCollection<IDbCommand>(commands);
        }

        /// <summary>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> involved.</summary>
        public TDomainEntity DomainEntity
        {
            get
            {
                return _DomainEntity;
            }
        }

        /// <summary>The <see cref="IDbCommand" />s involved.</summary>
        public ReadOnlyCollection<IDbCommand> Commands
        {
            get
            {
                return _Commands;
            }
        }

        /// <summary>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> involved.</summary>
        private TDomainEntity _DomainEntity;
        /// <summary>The <see cref="IDbCommand" />s involved.</summary>
        private ReadOnlyCollection<IDbCommand> _Commands;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Ancestor of a database oriented <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataMapper']/*" /></summary>
    /// <example>
    ///   <para>This sample uses the sample classes defined for <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}"/>.
    /// It maps the sample <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> to a database table defined as :
    ///     <code>
    /// CREATE TABLE Persons (
    ///     Id INT NOT NULL IDENTITY(1, 1),
    ///     Name VARCHAR(50) NOT NULL,
    ///     PRIMARY KEY (Id)
    /// );
    ///     </code>
    ///   </para>
    ///   <code>
    /// public partial class PersonMapper:
    ///     DataMapper&lt;Person, PersonPrimaryKey, PersonDataTransfer&gt;,
    ///     IPersonMapper
    /// {
    ///     public PersonMapper(DbConnection connection, ICacheManager cacheManager)
    ///         base(connection, DomainEntityCreator.CreateDefault&lt;Person&gt;(), cacheManager)
    ///     {
    ///     }
    ///
    ///     public DomainEntityKeyedCollection&lt;Person&gt; FindAll(DataMapperCollection dataMappers)
    ///     {
    ///         IDbCommand[] commands=new IDbCommand[1];
    ///
    ///         commands[0]=Connection.CreateCommand();
    ///         commands[0].CommandType=CommandType.Text;
    ///         commands[0].CommandText="SELECT * FROM Persons";
    ///
    ///         return Load(commands, dataMappers);
    ///     }
    ///
    ///     protected override IList&lt;IDbCommand&gt; CreateSelectCommands(PersonPrimaryKey key)
    ///     {
    ///         IDbCommand[] commands=new IDbCommand[1];
    ///
    ///         commands[0]=Connection.CreateCommand();
    ///         commands[0].CommandType=CommandType.Text;
    ///         commands[0].CommandText="SELECT * FROM Persons WHERE Id=@Id";
    ///
    ///         IDbDataParameter p=command.CreateParameter();
    ///         p.ParameterName="@Id";
    ///         p.DbType=DbType.Int32;
    ///         p.Value=key.Value;
    ///         p.Direction=ParameterDirection.Input;
    ///         command.Parameters.Add(p);
    ///
    ///         return commands;
    ///     }
    ///
    ///     protected override IList&lt;IDbCommand&gt; CreateDeleteCommands(PersonDataTransfer data)
    ///     {
    ///         IDbCommand[] commands=new IDbCommand[1];
    ///
    ///         commands[0]=Connection.CreateCommand();
    ///         commands[0].CommandType=CommandType.Text;
    ///         commands[0].CommandText="DELETE FROM Persons WHERE Id=@Id";
    ///
    ///         IDbDataParameter p=commands[0].CreateParameter();
    ///         p.ParameterName="@Id";
    ///         p.DbType=DbType.Int32;
    ///         p.Value=data.Id;
    ///         p.Direction=ParameterDirection.Input;
    ///         commands[0].Parameters.Add(p);
    ///
    ///         return commands;
    ///     }
    ///
    ///     protected override IList&lt;IDbCommand&gt; CreateInsertCommands(PersonDataTransfer data)
    ///     {
    ///         IDbCommand[] commands=new IDbCommand[1];
    ///
    ///         commands[0]=Connection.CreateCommand();
    ///         commands[0].CommandType=CommandType.Text;
    ///         commands[0].CommandText="INSERT INTO Persons VALUES(@Name)";
    ///
    ///         IDbDataParameter p=commands[0].CreateParameter();
    ///         p.ParameterName="@Name";
    ///         p.DbType=DbType.String;
    ///         p.Value=data.Name;
    ///         p.Direction=ParameterDirection.Input;
    ///         commands[0].Parameters.Add(p);
    ///
    ///         return commands;
    ///     }
    ///
    ///     protected override IList&lt;IDbCommand&gt; CreateUpdateCommands(PersonDataTransfer data)
    ///     {
    ///         IDbCommand[] commands=new IDbCommand[1];
    ///
    ///         commands[0]=Connection.CreateCommand();
    ///         commands[0].CommandType=CommandType.Text;
    ///         commands[0].CommandText="UPDATE Persons SET Name=@Name WHERE Id=@Id";
    ///
    ///         IDbDataParameter p=commands[0].CreateParameter();
    ///         p.ParameterName="@Name";
    ///         p.DbType=DbType.String;
    ///         p.Value=data.Name;
    ///         p.Direction=ParameterDirection.Input;
    ///         commands[0].Parameters.Add(p);
    ///
    ///         p=commands[0].CreateParameter();
    ///         p.ParameterName="@Id";
    ///         p.DbType=DbType.Int32;
    ///         p.Value=data.Id;
    ///         p.Direction=ParameterDirection.Input;
    ///         commands[0].Parameters.Add(p);
    ///
    ///         return commands;
    ///     }
    ///
    ///     protected override PersonDataTransfer GetDataTransferObject(IList&lt;IDataRecord&gt; records)
    ///     {
    ///         PersonDataTransfer data=new PersonDataTransfer();
    ///
    ///         int ord=record.GetOrdinal("Id");
    ///         if (!record.IsDBNull(ord))
    ///             data.Id=record.GetInt32(ord);
    ///
    ///         ord=record.GetOrdinal("Name");
    ///         if (!record.IsDBNull(ord))
    ///             data.Name=record.GetString(ord);
    ///
    ///         return data;
    ///     }
    ///
    ///     protected override void OnInserted(Person domainModel, IList&lt;IDbCommand&gt; commands)
    ///     {
    ///         IDbCommand command=Connection.CreateCommand();
    ///         commands.CommandType=CommandType.Text;
    ///         commands.CommandText="SELECT @@IDENTITY";
    ///         command.Transaction=commands[0].Transaction;
    ///
    ///         domainModel.SetId(Convert.ToInt32(command.ExecuteScalar()));
    ///
    ///         base.OnInserted(domainModel, commands);
    ///     }
    /// }
    ///   </code>
    /// </example>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public abstract class DataMapper<TDomainEntity, TPrimaryKey, TDataTransferObject>:
        Salamanca.DataAccess.DataMapperBase<TDomainEntity, TPrimaryKey, TDataTransferObject>
        where TDomainEntity:
            DomainEntity<TPrimaryKey, TDataTransferObject>
        where TPrimaryKey:
            IPrimaryKey,
            new()
        where TDataTransferObject:
            class, // SOAP serialization does not accept structs...
            IDataTransferObject,
            new()
    {



        /// <summary>Constructor.</summary>
        /// <param name="connection">The database connection used to deal with the data </param>
        /// <param name="instanceCreator">Member used to create new instances for the type.</param>
        /// <param name="cacheManager">A cache manager. Must not be <c>null</c>.</param>
        protected DataMapper(DbConnection connection, DomainEntityCreator<TDomainEntity> instanceCreator, ICacheManager cacheManager):
            base(instanceCreator, cacheManager)
        {
            _Connection=connection;
        }



        /// <summary>Finds the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance with the specified key.</summary>
        //        /// <param name="key">The key against which the command is created </param>
        public override sealed TDomainEntity Find(TPrimaryKey key, DataMapperCollection dataMappers)
        {
            TDomainEntity ret=null;

            ret=GetInstance(key, null);
            if (ret==null)
            {
                DomainEntityKeyedCollection<TDomainEntity> found=Load(CreateSelectCommands(key), dataMappers);
                if (found.Contains(key))
                    ret=found[key];
            }

            return ret;
        }



        /// <summary>Refreshes the data of the specified <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance from the database.</summary>
        /// <param name="domainModel">The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance to be refreshed.</param>
        protected override sealed void Refresh(TDomainEntity domainModel)
        {
            Debug.Assert(domainModel!=null);
            if (domainModel==null)
                throw new ArgumentNullException("domainModel");

            using (DbConnectionAdapter cnx=new DbConnectionAdapter(_Connection, true))
            {
                cnx.Open();

                using (DisposableCollection<IDbCommand> commands=new DisposableCollection<IDbCommand>(CreateSelectCommands((TPrimaryKey)((IDomainEntity)domainModel).Id)))
                {
                    OnRefreshing(domainModel, commands);

#if (!NET_CF)
                    foreach (IDbCommand command in commands)
                        Traces.TraceBeforeCommand(Configuration.Switch, command, "ExecuteReader");
#endif
                    using (DataReaderCollection<IDataReader> readers=new DataReaderCollection<IDataReader>())
                    {
                        foreach (IDbCommand command in commands)
                        {
                            command.Connection=cnx.Connection;
                            command.Prepare();

                            readers.Add(command.ExecuteReader());
#if (!NET_CF)
                            Traces.TraceAfterCommand(Configuration.Switch, command, "ExecuteReader");
#endif
                        }

                        // Get the first result.
                        // If no data was found, an exception is raised.
                        readers.Read();
#if (!NET_CF)
                        if (Configuration.Switch.TraceVerbose)
                            foreach (IDataReader reader in readers)
                            {
                                object[] tmp=new object[reader.FieldCount];
                                reader.GetValues(tmp);
                                Trace.WriteLine(Traces.GetValueTrace(tmp));
                            }
#endif

                        // Fill the instance with data
                        TransferData(domainModel, GetDataTransferObject(readers.DataRecords));
                    }

                    OnRefreshed(domainModel, commands);
                }
            }
        }



        /// <summary>Creates a list of database commands that returns the data for the instance with the specified key.</summary>
        /// <param name="key">The key against which the command is created </param>
        /// <returns>The database commands.</returns>
        protected abstract IList<IDbCommand> CreateSelectCommands(TPrimaryKey key);

        /// <summary>Creates a list of database commands that deletes the data of the instance.</summary>
        /// <param name="data">The data to be deleted.</param>
        /// <returns>The database commands.</returns>
        protected abstract IList<IDbCommand> CreateDeleteCommands(TDataTransferObject data);

        /// <summary>Creates a list of database command that inserts the data of the instance.</summary>
        /// <param name="data">The data to be inserted.</param>
        /// <returns>The database commands.</returns>
        protected abstract IList<IDbCommand> CreateInsertCommands(TDataTransferObject data);

        /// <summary>Creates a list of database command that updates the data of the instance.</summary>
        /// <param name="data">The data to be updated.</param>
        /// <returns>The database commands.</returns>
        protected abstract IList<IDbCommand> CreateUpdateCommands(TDataTransferObject data);



        /// <summary>Gets the <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the specified data.</summary>
        /// <param name="records">The records containing the data.</param>
        /// <returns>The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" /> containing the data.</returns>
        protected abstract TDataTransferObject GetDataTransferObject(IList<IDataRecord> records);



        /// <summary>Returns the list of <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instances
        /// returned by the specified database <paramref name="cmds">commands</paramref>.</summary>
        protected DomainEntityKeyedCollection<TDomainEntity> Load(IList<IDbCommand> cmds, DataMapperCollection dataMappers)
        {
            List<TDomainEntity> ret=new List<TDomainEntity>();

            using (DbConnectionAdapter cnx=new DbConnectionAdapter(_Connection, true))
            {
                cnx.Open();

                using (DisposableCollection<IDbCommand> commands=new DisposableCollection<IDbCommand>(cmds))
                {
#if (!NET_CF)
                    foreach (IDbCommand command in commands)
                        Traces.TraceBeforeCommand(Configuration.Switch, command, "ExecuteReader");
#endif
                    using (DataReaderCollection<IDataReader> readers=new DataReaderCollection<IDataReader>())
                    {
                        foreach (IDbCommand command in commands)
                        {
                            command.Connection=cnx.Connection;
                            command.Prepare();

                            readers.Add(command.ExecuteReader());
#if (!NET_CF)
                            Traces.TraceAfterCommand(Configuration.Switch, command, "ExecuteReader");
#endif
                        }

                        while (readers.Read())
                        {
#if (!NET_CF)
                            if (Configuration.Switch.TraceVerbose)
                                foreach (IDataReader reader in readers)
                                {
                                    object[] tmp=new object[reader.FieldCount];
                                    reader.GetValues(tmp);
                                    Trace.WriteLine(Traces.GetValueTrace(tmp));
                                }
#endif

                            // Creation of the Domain Entity instance
                            TDataTransferObject data=GetDataTransferObject(readers.DataRecords);
                            TDomainEntity obj=CreateDomainEntity(data);
                            ((IDomainEntity)obj).DataMappers=dataMappers;

                            // Fill the instance with data
                            TransferData(obj, data);

                            // Update the result set
                            TPrimaryKey key=(TPrimaryKey)((IDomainEntity)obj).Id;
                            ret.Add(GetInstance(key, obj));
                        }
                    }
                }
            }

            return new DomainEntityKeyedCollection<TDomainEntity>(ret);
        }



        /// <summary>Deletes the data of the specified <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance.</summary>
//        /// <param name="domainModel">The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance to be deleted.</param>
        protected override void Delete(TDomainEntity domainModel, IPersistenceContext context)
        {
            Debug.Assert(context!=null);
            if (context==null)
                throw new ArgumentNullException("context");
            Debug.Assert(domainModel!=null);
            if (domainModel==null)
                throw new ArgumentNullException("domainModel");
            Debug.Assert(!((IDomainEntity)domainModel).IsNew);
            if (((IDomainEntity)domainModel).IsNew)
                throw new InvalidOperationException(Resources.SR.CannotDeleteNewDomainEntity);

            using (DbConnectionAdapter cnx=new DbConnectionAdapter(Connection))
            {
                cnx.Open();

                domainModel.TriggerDeletingEvent(new PersistenceEventArgs(context));

                if (context.ForceAction || !((IDomainEntity)domainModel).IsNew)
                {
                    Transaction transaction=context.Transaction as Transaction;
#if (!NET_CF)
                    if (transaction!=null)
                        cnx.EnlistTransaction(transaction);
#endif

                    using (DisposableCollection<IDbCommand> commands=new DisposableCollection<IDbCommand>(CreateDeleteCommands((TDataTransferObject)((ITransferable)domainModel).GetData())))
                    {
                        if (transaction!=null)
                            foreach (IDbCommand command in commands)
                                if (command.Transaction==null)
                                    command.Transaction=(DbTransaction)transaction;

                        TPrimaryKey key=(TPrimaryKey)((IDomainEntity)domainModel).Id;

                        DoDeleting(domainModel, commands, context);

                        foreach (IDbCommand command in commands)
                        {
#if (!NET_CF)
                            Traces.TraceBeforeCommand(Configuration.Switch, command, "ExecuteNonQuery");
#endif
                            command.Prepare();
                            command.ExecuteNonQuery();
#if (!NET_CF)
                            Traces.TraceAfterCommand(Configuration.Switch, command, "ExecuteNonQuery");
#endif
                        }

                        DoDeleted(domainModel, commands, context);

                        CacheManager.Remove(key);
                    }
                }

                domainModel.TriggerDeletedEvent(new PersistenceEventArgs(context));
            }

        }



        /// <summary>Saves the data of the specified <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance.</summary>
//        /// <param name="domainModel">The <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> instance to be saved.</param>
        protected override void Save(TDomainEntity domainModel, IPersistenceContext context)
        {
            Debug.Assert(context!=null);
            if (context==null)
                throw new ArgumentNullException("context");
            Debug.Assert(domainModel!=null);
            if (domainModel==null)
                throw new ArgumentNullException("domainModel");

            using (DbConnectionAdapter cnx=new DbConnectionAdapter(Connection))
            {
                cnx.Open();

                domainModel.TriggerSavingEvent(new PersistenceEventArgs(context));

                if (context.ForceAction || (domainModel.IsModified && (((IDomainEntity)domainModel).IsNew || ((IDomainEntity)domainModel).IsLoaded)))
                {
                    if (!domainModel.Validate())
                        throw new BrokenRulesException(((IValidatable)domainModel).GetBrokenRules(), domainModel);

                    Transaction transaction=context.Transaction as Transaction;
#if (!NET_CF)
                    if (transaction!=null)
                        cnx.EnlistTransaction(transaction);
#endif

                    // Create the commands
                    List<IDbCommand> cmds=new List<IDbCommand>();

                    if (((IDomainEntity)domainModel).IsNew)
                        DoInserting(domainModel, cmds, context);
                    else
                        DoUpdating(domainModel, cmds, context);

                    if (((IDomainEntity)domainModel).IsNew)
                        cmds.AddRange(CreateInsertCommands((TDataTransferObject)((ITransferable)domainModel).GetData()));
                    else
                        cmds.AddRange(CreateUpdateCommands((TDataTransferObject)((ITransferable)domainModel).GetData()));

                    using (DisposableCollection<IDbCommand> commands=new DisposableCollection<IDbCommand>(cmds))
                    {
                        if (transaction!=null)
                            foreach (IDbCommand command in commands)
                                if (command.Transaction==null)
                                    command.Transaction=(DbTransaction)transaction;

                        // Execution
                        foreach (IDbCommand command in commands)
                        {
#if (!NET_CF)
                            Traces.TraceBeforeCommand(Configuration.Switch, command, "ExecuteNonQuery");
#endif
                            command.Prepare();
                            command.ExecuteNonQuery();
#if (!NET_CF)
                            Traces.TraceAfterCommand(Configuration.Switch, command, "ExecuteNonQuery");
#endif
                        }

                        if (((IDomainEntity)domainModel).IsNew)
                        {
                            DoInserted(domainModel, commands, context);
                            CacheManager.Add((TPrimaryKey)((IDomainEntity)domainModel).Id, domainModel);
                        } else
                            DoUpdated(domainModel, commands, context);
                    }
                }

                domainModel.TriggerSavedEvent(new PersistenceEventArgs(context));
            }

        }

        private void DoDeleted(TDomainEntity domainModel, IList<IDbCommand> commands, IPersistenceContext context)
        {
            domainModel.SetNew(true);
            domainModel.SetLoaded(false);

            OnDeleted(domainModel, commands);
        }

        private void DoInserted(TDomainEntity domainModel, IList<IDbCommand> commands, IPersistenceContext context)
        {
            domainModel.SetModified(false);
            domainModel.SetNew(false);
            domainModel.SetLoaded(true);

            OnInserted(domainModel, commands);
        }

        private void DoUpdated(TDomainEntity domainModel, IList<IDbCommand> commands, IPersistenceContext context)
        {
            domainModel.SetModified(false);

            OnUpdated(domainModel, commands);
        }

        private void DoDeleting(TDomainEntity domainModel, IList<IDbCommand> commands, IPersistenceContext context)
        {
            OnDeleting(domainModel, commands);
        }

        private void DoInserting(TDomainEntity domainModel, IList<IDbCommand> commands, IPersistenceContext context)
        {
            OnInserting(domainModel, commands);
        }

        private void DoUpdating(TDomainEntity domainModel, IList<IDbCommand> commands, IPersistenceContext context)
        {
            OnUpdating(domainModel, commands);
        }



        /// <summary>Raises the <see cref="Refreshed" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnRefreshed(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Refreshed!=null)
                Refreshed(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));

            domainModel.TriggerRefreshedEvent(new EventArgs());
        }

        /// <summary>Raises the <see cref="Refreshing" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnRefreshing(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Refreshing!=null)
                Refreshing(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));

            domainModel.TriggerRefreshingEvent(new EventArgs());
        }

        /// <summary>Raises the <see cref="Deleted" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnDeleted(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Deleted!=null)
                Deleted(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));
        }



        /// <summary>Raises the <see cref="Inserted" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnInserted(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Inserted!=null)
                Inserted(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));
        }



        /// <summary>Raises the <see cref="Updated" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnUpdated(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Updated!=null)
                Updated(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));
        }



        /// <summary>Raises the <see cref="Deleting" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnDeleting(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Deleting!=null)
                Deleting(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));
        }



        /// <summary>Raises the <see cref="Inserting" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnInserting(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Inserting!=null)
                Inserting(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));
        }



        /// <summary>Raises the <see cref="Updating" /> event.</summary>
        /// <remarks>
        ///   <include file="../../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnUpdating(TDomainEntity domainModel, IList<IDbCommand> commands)
        {
            if (Updating!=null)
                Updating(this, new DataMapperEventArgs<TDomainEntity>(domainModel, commands));
        }



        /// <summary>Gets the database connection.</summary>
        /// <value>The database connection.</value>
        protected IDbConnection Connection
        {
            get
            {
                return _Connection;
            }
        }



        /// <summary>The database connection.</summary>
        private IDbConnection _Connection;

        /// <summary>Occurs when a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> has been <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Refresh">refreshed</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Refreshed;
        /// <summary>Occurs when a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> is about to be <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Refresh">refreshed</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Refreshing;
        /// <summary>Occurs when a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> has been <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Delete()">deleted</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Deleted;
        /// <summary>Occurs when a <see cref="IDomainEntity.IsNew">new</see> <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> has been <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Save()">saved</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Inserted;
        /// <summary>Occurs when a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> has been <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Save()">saved</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Updated;
        /// <summary>Occurs when a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> is about to be <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Delete()">deleted</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Deleting;
        /// <summary>Occurs when a <see cref="IDomainEntity.IsNew">new</see> <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> is about to be <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Save()">saved</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Inserting;
        /// <summary>Occurs when a <include file="../../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> is about to be <see cref="DomainEntity{TPrimaryKey,TDataTransferObject}.Save()">saved</see>.</summary>
        public event EventHandler<DataMapperEventArgs<TDomainEntity>> Updating;

    }
}
